fix(widget): show context-limit message on HTTP 429 token budget exceeded and disable composer - #62
Merged
AmitAvital1 merged 7 commits intoAug 1, 2026
Conversation
Let a user see their past conversations, start a new one, and switch between them, backed by the agent manager as the source of truth. Backend: - Repository.list_sessions(user_id) + rename_session (port, memory, sql) - GET /conversations?user_id=... returns id, title, last_message_at (most-recently-active first); ConversationService.list_conversations - thread_title() derives a title from the first user message; the service sets it on the first turn of a conversation Widget (stateless; BE owns the list and titles): - anonymous per-browser user id in localStorage, sent on create/list; an optional <agent-chat user="..."> attribute overrides it, leaving the seam for real host identity later - AgentChatClient.listConversations + useConversation listThreads/ switchTo/startNew - header history + new-chat buttons and a slide-over thread drawer; switching loads that thread's messages and usage; drawer is inert when closed. Degrades to empty list against a backend without the endpoint Tests: listing scoped by user, auto-title, and a widget e2e for the drawer (list, switch, new chat). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… user Review feedback on extra-org#59, all three points: 1. The widget remembered the active conversation under a key built from the endpoint alone, so a second user on the same browser (shared machine, or a host app signing someone else in via `<agent-chat user="...">`) resumed the previous user's chat. The key is now `agent-chat:<endpoint>:<userId>`. 2. `user_id` was sent when a conversation was created but not on send/stream, so the run executed with `RunContext.user_id = None` — exactly the field hooks and tools authorize on. The service now loads the session and takes the identity from it: a caller that omits user_id runs as the session's owner, and one that supplies a *different* user_id is refused with ConversationAccessDenied (403) instead of being silently rebound. The browser no longer decides who it is on a turn. 3. `GET /conversations?user_id=` stays a scoping parameter, not an authorization boundary — now stated in the route's docstring, with the place to put real auth. `_require` returns the session it already had to fetch, so the ownership check costs no extra query. Tests: a turn with no client user_id runs as the owner; a mismatched user_id raises and persists nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Show a bouncing-dots indicator while the assistant has no answer text yet, and keep it visible when the user leaves a streaming thread and returns. Messages now live in a per-conversation map keyed by the id captured at submit time, so switching threads only swaps the view — the in-flight stream keeps writing to its own bucket. `typing` means "stream in flight" (set at submit, cleared at completion); reduceStreamEvent is reduced to content accumulation only, fixing dots that could otherwise linger on an empty final answer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review feedback on extra-org#60: stream(), send(), loadHistory() and loadUsage() each re-read the active conversation id from localStorage when they ran. A turn that started in thread A but finished after the user switched to B could retry, or refresh usage, against B. Storage now only remembers which thread was selected last; it never decides where an in-flight request goes. - Conversation methods take the id: send(id, text), stream(id, text), loadHistory(id), loadUsage(id). The app already captures the id at submit time for its per-conversation message map, so it passes that same id. - Usage moves into a per-conversation map alongside messages, so a late response cannot paint another thread's number. - Stale-conversation recovery (404 -> create a new one) now reports the replacement through `onReplaced`, and the app re-keys that thread's messages onto the id the turn actually ran under instead of leaving them stranded under the dead one. Test: a turn whose stream fails after the user switched threads retries on its own conversation, and the usage refresh follows it — both landed on the other thread before this change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AmitAvital1
reviewed
Jul 29, 2026
rishu685
force-pushed
the
fix/issue-50-budget-exceeded-message
branch
2 times, most recently
from
July 29, 2026 11:46
85d2868 to
45e5520
Compare
AmitAvital1
reviewed
Jul 29, 2026
AmitAvital1
reviewed
Jul 29, 2026
AmitAvital1
requested changes
Jul 29, 2026
AmitAvital1
left a comment
Collaborator
There was a problem hiding this comment.
Can you please rebase your branch fix on top of this feat/widget-message-loading working branch (#60)?
And please attach screenshot that its work well now.
thanks
…eded and disable composer
rishu685
force-pushed
the
fix/issue-50-budget-exceeded-message
branch
from
July 29, 2026 19:28
45e5520 to
e1c25e9
Compare
Contributor
Author
Collaborator
|
Do not merge until parent merge!! |
AmitAvital1
force-pushed
the
feat/widget-message-loading
branch
3 times, most recently
from
July 31, 2026 14:19
a10fb7b to
3fdbe7c
Compare
Collaborator
|
Hi, can you please rebase again on top of main and resolve conflicts? so we can merge it. thanks |
Contributor
Author
|
Done! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Closes #50.
This PR implements a backend-driven solution to display a clear, user-friendly context-limit error message when a conversation hits its token budget (HTTP 429
ConversationTokenBudgetExceeded), rather than the generic"Something went wrong. Please try again."message.To address reviewer concerns from PR #51 and #53 (statelessness of the frontend), we propagate the backend's explicit JSON
detailerror payload to the widget, allowing the backend to dictate the error message format directly.Changes
"This conversation has reached its context limit. Start a new chat to continue.".2.Backend Test: Added Python unit tests checking 429 response codes and custom detail payloads in stream and non-stream endpoints.
detailfield and attach it toAgentChatHttpError.4.Frontend UI:
"Context limit reached."so the user cannot retry message turns on an exhausted session.Verification
pytestpassed: 539 passed